#pragma once

#include "../../base/obj_pointer.h"
#include "gl.h"
#include <functional>

OPEN_JLIB_NS


struct BufferObject : Obj {

  enum class BufferType : GLenum {
    VertexAttributes = GL_ARRAY_BUFFER,
    VertexIndices = GL_ELEMENT_ARRAY_BUFFER,
    UniformBlock = GL_UNIFORM_BUFFER,
  };

  enum class BufferMode : GLenum {
    Constant = GL_STATIC_DRAW,
    Variable = GL_DYNAMIC_DRAW,
    Volatile = GL_STREAM_DRAW,
  };

  enum class AccessMode : GLenum {
    ReadOnly = GL_READ_ONLY,
    WriteOnly = GL_WRITE_ONLY,
    ReadWrite = GL_READ_WRITE,
  };



  ~BufferObject () override;

  bool init (BufferType type, uwl size, BufferMode mode = BufferMode::Volatile, const void* data = nullptr);

  void set (swl offset, uwl size, const void* data);

  void use ();

  void clear ();


  inline bool valid () const noexcept { return _id != 0; }
  inline GLuint id () const noexcept { return _id; }


  template<typename Type>
  void map (const std::function<void(Type*)>& writer, AccessMode mode = AccessMode::WriteOnly) {
    auto ptr = (Type*)_map((GLenum)mode);
    writer(ptr);
    _unmap();
  }

  template<typename SType, typename MType>
  void set (const MType& value, MType SType::*member) {
    auto object = (SType*)0;
    auto offset = (GLintptr)(&(object->*member));
    set(offset, sizeof(MType), &value);
  }

private:
  void* _map (GLenum mode);
  void _unmap ();

  GLuint _id {0};
  GLenum _type;
};


CLOSE_JLIB_NS


